Skip to content

feat(workflow): add workflow automation support - #10221

Open
amir-samiee wants to merge 16 commits into
2dust:masterfrom
amir-samiee:feat/workflow
Open

amir-samiee wants to merge 16 commits into
2dust:masterfrom
amir-samiee:feat/workflow

Conversation

@amir-samiee

@amir-samiee amir-samiee commented Sep 23, 2026 •

Copy link
Copy Markdown

Summary

  • add workflow execution runtime, actions, step options, and persisted workflow models
  • integrate workflow management and execution with the main, profiles, and subscription view models
  • add workflow editing and runner views for WPF and Avalonia
  • add workflow handler coverage

Why?

I found myself doing more or less the same sequence of actions almost every time I needed the program; and sometimes the items of each step (e.g. servers, subscriptions) were as high as I needed to go and come back to check whether the last step has been completed. As these steps were slightly (or occasionally quite) different based on situation/needs, I found no better way to use the already present features of v2rayN in a workflow-style manner

Testing

  • built and tested on Windows 11 (x64) only. Results: successful
  • Added WorkflowHandlerTests coverage for workflow behavior.

@amir-samiee
amir-samiee marked this pull request as ready for review September 23, 2026 14:53
@2dust

2dust commented Oct 17, 2026

Copy link
Copy Markdown
Owner

非常感谢 PR,但是暂时还没有考虑合并此功能。
虽然功能是非常好的,但是在代码上有些复杂,且没有复用当前的代码;
如果要合并,可能需要重构很多功能,保证一些功能代码具有唯一性。

@amir-samiee

Copy link
Copy Markdown
Author

Thank you very much for your review. Since the request was for some while ago I'm not sure if I'd make the changes you suggested right away; but in case I did, I will absolutely make sure to take the points you made into account.

Thanks again for your time.

@2dust

2dust commented Nov 3, 2026

Copy link
Copy Markdown
Owner

要大版本升级了,所以计划合并。

  1. 请把菜单从订阅分组中转到主界面,因为这个是独立功能
  2. 最近合并了非常多的代码,请 rebase 代码测试下,特别是 avalonia 版本 aot

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Unresolved workflow bugs can block saves with duplicate subscription names, incorrectly scope all-group operations, and advance steps before server activation or proxy changes complete.

8 open issues
1 file not reviewed
  • v2rayN/ServiceLib/Resx/ResUI.Designer.cs: Generated file
What changed in this PR

Adds reusable workflows to automate recurring subscription, server-management, testing, and proxy operations through the shared service layer and both desktop interfaces.

Changes:

  • Adds persisted workflow definitions, configurable steps, and sequential execution through a runtime interface.
  • Adds workflow management and editing windows in WPF and Avalonia, accessible from the main and profiles views.
  • Adds tests for serialization, parameter options, action dispatch, and disabled-step handling.
File Description
v2rayN/​v2rayN/​Views/​WorkflowWindow.xaml.cs Binds WPF workflow commands and dialogs.
v2rayN/​v2rayN/​Views/​WorkflowWindow.xaml Defines the WPF workflow manager.
v2rayN/​v2rayN/​Views/​WorkflowEditWindow.xaml.cs Binds WPF workflow editing controls.
v2rayN/​v2rayN/​Views/​WorkflowEditWindow.xaml Defines the WPF step editor.
v2rayN/​v2rayN/​Views/​ProfilesView.xaml.cs Binds the workflow shortcut.
v2rayN/​v2rayN/​Views/​ProfilesView.xaml Adds a workflow toolbar button.
v2rayN/​v2rayN/​Views/​MainWindow.xaml.cs Binds the workflow menu command.
v2rayN/​v2rayN/​Views/​MainWindow.xaml Adds the workflow menu entry.
v2rayN/​v2rayN/​Common/​SimpleViewLocator.cs Registers WPF workflow windows.
v2rayN/​v2rayN.Desktop/​Views/​WorkflowWindow.axaml.cs Binds Avalonia workflow commands and dialogs.
v2rayN/​v2rayN.Desktop/​Views/​WorkflowWindow.axaml Defines the Avalonia workflow manager.
v2rayN/​v2rayN.Desktop/​Views/​WorkflowEditWindow.axaml.cs Binds Avalonia step-editing commands.
v2rayN/​v2rayN.Desktop/​Views/​WorkflowEditWindow.axaml Defines the Avalonia step editor.
v2rayN/​v2rayN.Desktop/​Views/​ProfilesView.axaml.cs Binds the workflow shortcut.
v2rayN/​v2rayN.Desktop/​Views/​ProfilesView.axaml Adds a workflow toolbar button.
v2rayN/​v2rayN.Desktop/​Views/​MainWindow.axaml.cs Binds the workflow menu command.
v2rayN/​v2rayN.Desktop/​Views/​MainWindow.axaml Adds the workflow menu entry.
v2rayN/​v2rayN.Desktop/​DesignData/​DesignData.cs Adds workflow design-time view models.
v2rayN/​v2rayN.Desktop/​Common/​SimpleViewLocator.cs Registers Avalonia workflow windows.
v2rayN/​ServiceLib/​ViewModels/​WorkflowViewModel.cs Manages workflow listing, editing, deletion, and execution.
v2rayN/​ServiceLib/​ViewModels/​WorkflowRuntime.cs Connects workflow actions to application operations.
v2rayN/​ServiceLib/​ViewModels/​WorkflowRunner.cs Registers the runtime and starts workflows.
v2rayN/​ServiceLib/​ViewModels/​WorkflowEditViewModel.cs Manages step editing and workflow saving.
v2rayN/​ServiceLib/​ViewModels/​SubSettingViewModel.cs Publishes server-state changes after subscription deletion.
v2rayN/​ServiceLib/​ViewModels/​ProfilesViewModel.cs Adds group-scoped workflow operations and a shortcut command.
v2rayN/​ServiceLib/​ViewModels/​MainWindowViewModel.cs Opens workflow management and registers its runtime.
v2rayN/​ServiceLib/​Resx/​ResUI.resx Adds workflow labels and notifications.
v2rayN/​ServiceLib/​Resx/​ResUI.Designer.cs Exposes workflow resource strings.
v2rayN/​ServiceLib/​Models/​Entities/​WorkflowItem.cs Defines persisted workflow records.
v2rayN/​ServiceLib/​Models/​Dto/​WorkflowStep.cs Defines step data and editor-facing properties.
v2rayN/​ServiceLib/​Manager/​AppManager.cs Creates workflow storage and provides queries.
v2rayN/​ServiceLib/​Helper/​WorkflowStepOptions.cs Defines parameter choices, defaults, and normalization.
v2rayN/​ServiceLib/​Handler/​WorkflowHandler.cs Serializes steps and dispatches workflow actions.
v2rayN/​ServiceLib/​Handler/​ConfigHandler.cs Adds workflow persistence and deletion.
v2rayN/​ServiceLib/​Events/​AppEvents.cs Adds workflow-opening and server-state events.
v2rayN/​ServiceLib/​Enums/​EWorkflowAction.cs Defines supported workflow actions.
v2rayN/​ServiceLib/​Enums/​EServerSelectType.cs Defines first/last server selection.
v2rayN/​ServiceLib/​Base/​IWorkflowRuntime.cs Defines the workflow execution interface.
v2rayN/​ServiceLib.Tests/​Handler/​WorkflowHandlerTests.cs Covers workflow data, options, and dispatch behavior.

Review effort: Balanced

Comment on lines +76 to +81
/// <summary>
/// Editor-only group name. <see cref="ViewModels.WorkflowEditViewModel"/> resolves it
/// back into <see cref="SubId"/> when the workflow is saved.
/// </summary>
[JsonIgnore]
public string? SubDisplay { get; set; }
RxSchedulers.MainThreadScheduler.Schedule(() => _ = SetSpeedTestResult(result));
await Task.CompletedTask;
});
await _speedtestService.RunLoop(actionType, lstSelected);
Comment on lines +723 to +725
var ordered = selectType == EServerSelectType.Last
? lstModel.OrderByDescending(t => ProfileExManager.Instance.GetSort(t.IndexId))
: lstModel.OrderBy(t => ProfileExManager.Instance.GetSort(t.IndexId));
{
return;
}
await SetDefaultServer(target.IndexId);
private async Task ResolveStepSubsAsync()
{
var subItems = await AppManager.Instance.SubItems() ?? [];
var map = subItems.ToDictionary(t => t.Remarks, t => t.Id);
Comment on lines +81 to +85
step.SubId = step.SubDisplay.IsNotEmpty()
&& step.SubDisplay != ResUI.AllGroupServers
&& map.TryGetValue(step.SubDisplay, out var id)
? id
: null;
public async Task<int> DeduplicateServers(string subId)
{
var count = await profiles.DedupServersAsync(subId);
NoticeManager.Instance.Enqueue(string.Format(ResUI.RemoveDuplicateServerResult, count, 0));
Comment on lines +45 to +46
AppEvents.SysProxyChangeRequested.Publish(type);
return Task.CompletedTask;
@amir-samiee

Copy link
Copy Markdown
Author

Hello dear @2dust
Thanks for your recommendation. I've already attempted to accomplish the modification tasks you mentioned but unfortunately due to some network flaws it hasn't been possible yet
I'll probably try again and keep you updated in the following days

amir-samiee and others added 13 commits November 5, 2026 19:44
Rebasing onto master broke workflow persistence: JsonUtils now resolves
types through the source-generated AppJsonContext, and WorkflowItem and
WorkflowStep were not registered there. Serialize returned an empty
string and Deserialize returned null, so saved workflows silently lost
every step. Register both types (and their lists).

Co-authored-by: openhands <openhands@all-hands.dev>
The editor loads subscription names asynchronously and assigns SubDisplay
after the step grid has already bound its group cell, so a plain
auto-property left the cell blank. Back it with a field and raise
PropertyChanged, matching the other editable step fields.

Co-authored-by: openhands <openhands@all-hands.dev>
SpeedtestService has no FastRealping case, so a workflow step that picked
it finished without testing anything. The interactive speedtest command
treats FastRealping as Realping; mirror that here so the step and the
command behave the same.

Co-authored-by: openhands <openhands@all-hands.dev>
SetDefaultServerForGroupAsync called ProfileExManager.GetSort per row,
and each call scans the whole ProfileEx bag, so ordering a group was
O(n*m) on the UI thread. Build the IndexId-to-Sort map once and order
with it.

Co-authored-by: openhands <openhands@all-hands.dev>
…roup

The editor's 'All groups' choice serializes to an empty SubId, but the
runner replaced an empty id with the currently selected subscription, so
an all-groups step silently scoped to one group. Pass null through to the
runtime (documented as every group) and drop the selected-group fallback,
including in the activation and test helpers. The runtime seam and its
callers now take a nullable group id; tests cover the all-groups case.

Co-authored-by: openhands <openhands@all-hands.dev>
Activation only published ReloadRequested, so the next workflow step ran
while the core was still restarting with the previous server. Add an
awaitable ReloadAsync hook wired to the main reload, and have the
activation path opt into it. The interactive command keeps firing the
request and returning, as before.

Co-authored-by: openhands <openhands@all-hands.dev>
DedupServersAsync returned the post-dedup total, and the notice filled
the removed field with it and hard-coded New to 0, so the message was
wrong. Return removed/old/new and pass both totals to the notice.

Co-authored-by: openhands <openhands@all-hands.dev>
SetSystemProxy only published an event and returned, so the next workflow
step ran before the OS proxy actually changed. Inject the status bar's
listener routine (now public) and await it, the same way subscription
updates are awaited.

Co-authored-by: openhands <openhands@all-hands.dev>
The workflow menu lived inside the Subscription menu, but workflows are
an independent feature. Give it its own top-level toolbar menu in both
the WPF and Avalonia main windows, matching the other top-level entries.
The ProfilesView toolbar button is unchanged.

Co-authored-by: openhands <openhands@all-hands.dev>
@amir-samiee

Copy link
Copy Markdown
Author

Hi @2dust, both points are addressed:

  1. Moved the workflow menu out of the Subscription group to a top-level entry in the main interface — for both the WPF and the Avalonia builds.
  2. Rebased onto current master and re-ran the tests (all passing). Also verified the Avalonia AOT publish succeeds.

Note: AI assistance was used for parts of this update.

<PathIcon Data="{StaticResource SemiIconPlus}" />
</Button.Content>
</Button>
<Button

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

主菜单已经有入口,这里可以不要了。

/// completion, including the core reload after activation and the OS proxy change, so a
/// following step never runs against a half-applied state.
/// </summary>
public sealed class WorkflowRuntime(

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WorkflowRuntime.cs 属于服务,请放到 Services中,可以改为 WorkflowRuntimeService

/// registers it after the main view models are built; a registration failure is
/// surfaced instead of silently doing nothing.
/// </summary>
public sealed class WorkflowRunner

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WorkflowRunner.cs 属于单例,请放到 Manager 中,可以改为 WorkflowRunnerManager

2dust and others added 3 commits November 6, 2026 14:22
The workflow menu is now a top-level entry in the main interface, so the
profiles toolbar button is a second entry point for the same action.
Remove it from both the WPF and the Avalonia profile views, along with
the now unused command bindings.

Co-authored-by: openhands <openhands@all-hands.dev>
WorkflowRuntime is a service, so move it to Services as
WorkflowRuntimeService. WorkflowRunner is a singleton, so move it to
Manager as WorkflowRunnerManager. Update the call sites and the log
category to the new names.

Co-authored-by: openhands <openhands@all-hands.dev>
@amir-samiee

Copy link
Copy Markdown
Author

@2dust Done

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants